#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <semaphore.h>
#include <pthread.h>
using namespace std;
// 环形队列
template <typename T>
class RingQueue
{
private:
    // P操作
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    // V操作
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    // 加锁
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    // 解锁
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    RingQueue(int cap)
        : _cap(cap), _ring_queue(cap), _productor_step(0), _consumer_step(0)
    {
        // 初始化信号量
        sem_init(&_room_sem, 0, _cap);
        sem_init(&_data_sem, 0, 0);
        // 初始化锁
        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }
    ~RingQueue()
    {
        // 销毁信号量
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
        // 销毁锁
        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }
    // 入队列---生产者写数据
    void Enqueue(const T &in)
    {
        // 申请空间信号量
        P(_room_sem);
        // 这里申请信号量成功之后,操作系统必定会为此线程分配资源供此线程使用,
        // 可以先分配资源,然后再对其加锁保护,
        // 先申请-拿到要处理的资源位置,再排队处理每个线程需要完成的操作
        // 加锁
        Lock(_productor_mutex);
        // 放数据
        _ring_queue[_productor_step] = in;
        // 处理下标
        _productor_step++;
        _productor_step %= _cap;
        // 解锁
        Unlock(_productor_mutex);
        // 释放数据信号量
        V(_data_sem);
    }
    // 出队列---消费者拿数据
    void Pop(T *out)
    {
        // 申请数据信号量
        P(_data_sem);
        // 加锁
        Lock(_consumer_mutex);
        // 拿数据
        *out = _ring_queue[_consumer_step];
        // 处理下标
        _consumer_step++;
        _consumer_step %= _cap;
        // 解锁
        Unlock(_consumer_mutex);
        // 释放空间信号量
        V(_room_sem);
    }

private:
    vector<T> _ring_queue;            // 环形队列
    int _cap;                         // 环形队列容量上限
    int _productor_step;              // 生产者下标
    int _consumer_step;               // 消费者下标
    sem_t _room_sem;                  // 空间信号量---生产者
    sem_t _data_sem;                  // 数据信号量---消费者
    pthread_mutex_t _productor_mutex; // 生产者锁
    pthread_mutex_t _consumer_mutex;  // 消费者锁
};